package com.yulan.bankback.aop;

import com.yulan.bankback.entity.model.admin.GeoIp;
import com.yulan.bankback.entity.model.admin.KindUser;
import com.yulan.bankback.entity.model.admin.webAttack.IpBlackList;
import com.yulan.bankback.entity.model.bank.BankUser;
import com.yulan.bankback.entity.result.RetCode;
import com.yulan.bankback.entity.result.RetResult;
import com.yulan.bankback.log.Logger;
import com.yulan.bankback.mapper.admin.KindUserMapper;
import com.yulan.bankback.mapper.admin.webAttack.IpBlackListMapper;
import com.yulan.bankback.mapper.bank.BankUserMapper;
import com.yulan.bankback.service.bank.RedisService;
import com.yulan.bankback.service.bank.RiskService;
import com.yulan.bankback.service.ip.IpService;
import com.yulan.bankback.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import net.ipip.ipdb.IPFormatException;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 接口鉴权切面
 */
@Component
@Slf4j
@Aspect
public class AuthAspect extends HandlerInterceptorAdapter  {

    @Autowired
    private RedisService redisService;

    @Autowired
    private RiskService riskService;

    @Autowired
    private IpService ipService;

    @Autowired
    private BankUserMapper bankUserMapper;

    @Autowired
    private KindUserMapper kindUserMapper;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private Logger logger;

    @Autowired
    private IpBlackListMapper ipBlackListMapper;

    @Value("#{'${bank.referer}'.split(',')}")
    private List<String> refererList;

    @Autowired
    @Qualifier("baseThreadPool")
    private ThreadPoolExecutor executor;

    @Pointcut("@annotation(com.yulan.bankback.aop.Authorization)")
    public void AuthorizationPointCut(){

    }

    @Around("AuthorizationPointCut()")
    public Object verifyAuth(ProceedingJoinPoint pjp) throws Throwable {
        Class<?> targetCls=pjp.getTarget().getClass();
        MethodSignature ms=(MethodSignature)pjp.getSignature();
        Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        Authorization authorization = method.getAnnotation(Authorization.class);

        String token = httpServletRequest.getHeader("token");
        String userType = httpServletRequest.getHeader("user_type");
        String ip = IpUtil.getIpAddr(httpServletRequest);

        if(StringUtils.isBlank(token)){
            return new RetResult<>().setCode(RetCode.UNAUTHORIZED).setMsg("没有权限访问该接口");
        }

        String target = redisService.get(token);

        if(target == null || !StringUtils.equals(target, "1")){
            return new RetResult<>().setCode(RetCode.UNAUTHORIZED).setMsg("认证已过期，请重新登陆");
        }

        //检测用户权限
        String[] values = authorization.value();
        List<String> userTypes = new ArrayList<>(Arrays.asList(values));
        Set<String> accessTypes = new HashSet<>(userTypes);

        //检测到有站内用户攻击后台系统
        if(!accessTypes.contains(userType)){
            String uid = redisService.get("token_" + token);
            logger.logAttack(ip, uid,"api_attack",  "warn");
            executor.execute(() -> {
                try {
                    GeoIp geoIp = ipService.findCityByIp(ip);
                    log.info("解析到底地址: {}", geoIp);
                    //插入黑名单IP地址
                    IpBlackList ipBlackList = new IpBlackList();
                    ipBlackList.setCreateTime(new Date());
                    ipBlackList.setCity(geoIp.getCityName());
                    ipBlackList.setAttackType("api攻击");
                    ipBlackList.setCountry(geoIp.getCountryName());
                    ipBlackListMapper.insert(ipBlackList);

                    //插入黑名单用户
                    BankUser bankUser = bankUserMapper.selectById(uid);
                    kindUserMapper.insert(KindUser.convertFromUser(bankUser, "black", "api攻击"));
                } catch (IPFormatException | IOException e) {
                    e.printStackTrace();
                }

            });
            return new RetResult<>().setCode(RetCode.UNAUTHORIZED).setMsg("没有权限访问该接口");
        }



        //检测csrf攻击
        String referer = httpServletRequest.getHeader("referer");
        log.info("refererList: {}, referer: {}, ip: {}, isContains: {} ",
                refererList, referer, ip, refererList.contains(referer));
        if(!refererList.contains(referer)){
            executor.execute(() -> {
                try {
                    String city = ipService.findCityByIp(ip).getCityName();
                    riskService.addAttackEvent(ip, "CSRF跨站脚本攻击", 3, city);
                } catch (IPFormatException | IOException e) {
                    e.printStackTrace();
                }

            });
            return new RetResult<>().setCode(RetCode.UNAUTHORIZED).setMsg("没有权限访问该接口");
        }



        return pjp.proceed();
    }


}
